跳到主要内容

3.2-Vue 搭配 TS

Create by fall on19 Sep 2022 Recently revised in 18 Mar 2024

在 vue 中使用 typescript,建议使用 Vite,因为 webpack 的分析有缺陷,且运行时类型检查需要占用额外的资源。

使用 TS 增强类型

以组合式 API 为主,即 <script lang="ts" setup> 中进行使用

defineProps

// 1. 使用 ts 定义 props
interface Props {
foo: string
bar?: number
}
const props = defineProps<Props>()
// 通过 ts 调用不能设置默认值,可以通过 withDefaults 传递默认值
interface Props {
foo: string
bar?: number
}
const props = withDefaults(defineProps<Props>(),{
foo: 'hello'
})
// 传递默认值也可以通过 *实验性的* 响应性语法糖:对 defineProps() 的响应性解构
// 默认值会被编译为等价的运行时选项
interface Props {
name: string
count?: number
}
const { name, count = 100 } = defineProps<Props>()

defineEmit

// 运行时
const emit = defineEmits(['change', 'update'])
// 基于类型
const emit = defineEmits<{
(e: 'change', id: number): void // 使用名称,传入的参数
(e: 'update', value: string): void
}>()

ref

import type {Ref} from 'vue'
const year1 = ref(2020) // 没有提供泛型,默认推断为 Ref<number>
const year2:Ref<string|number> = ref(2022)
// 指定泛型参数,但没有初始值,默认为 Ref<T,undefined>
const n = ref<number>() // 此处为 Ref<number|undefined>

reactive

interface Book{
title:string
publishYear?:number
}
const book:Book = reactive({
title:'成年人的谎言生活'
})
// reactive 中不推荐使用泛型,因为处理了深层次 ref 解包的返回值与泛型参数的类型不同。

computed

const count = ref(0)
const moreCount = computed<number>(()=>count.value+5) // 通过泛型来规定返回值的类型

事件

<template>
<input type="text" @change="onTextChange" />
</template>
<script lang="ts" setup>
// 为事件标注类型
function onTextChange(ev:Event){
(event.target as HTMLInputElement).value // 需要自行推断 event.target 的类型
}
</script>

provide

因为 provide 和 inject 会在不同的组件运行,所以 Vue 提供了 InjectionKey,继承自 Symbol

import {InjectionKey} from 'vue'
const key = Symbol() as InjectionKey<string>;
provide(key, 'foo')
const foo = inject(key)
// 不使用 symbol 注入,使用 string 类型作为 inject 注入,则需要传入泛型
const foo = inject<string>('foo') // 推断为 string | undefined
const fuu = inject<string>('fuu','bar') // 推断为 string | undefined

组件

子组件需要将内容暴露,父组件才能够使用

// 子组件 MyModal.vue
import { ref } from 'vue'
const isContentShown = ref(false)
const open = () => (isContentShown.value = true)
defineExpose({
open
})
// 父组件
// 如果想在 ts文件中使用,需要 volar 接管默认的 ts 设置,下方有讲到该问题
import MyModal from './MyModal.vue'
const modal = ref<InstanceType<typeof MyModal> | null>(null)
const openModal = () => {
modal.value?.open()
}

选项式 API

官方文档:https://cn.vuejs.org/guide/typescript/options-api.html

IDE 的一些问题

VS Code

在每个项目里我们都运行了两个语言服务实例:一个来自 Volar,一个来自 VSCode 的内置服务。在大型项目中可能会遇到表现不一致,或者是资源占用过高的问题。使用 Volar 接管模式,可以使用 Volar 同时接管 .ts.vue 文件。

在插件页面输入 @builtin typescript 改变默认插件的配置。

更改 JavaScript 和 TypeScript 的语言功能,在需要被 volar 接管的工作区禁用即可。

配置文件

手动配置 tsconfig.json 时,请必须保证以下选项设置为对应的值:

{
compilerOptions:{
isolatedModules:true, // 应设为 true,Vite 使用 esbuild 来转译 TypeScript,并受限于单文件转译的限制。
// 使用选项式 API 时应设为 true,或者至少设置 compilerOptions.noImplicitThis = true,它是 strict 模式的一部分,设置为 true 才可以获得对组件选项中 this 的类型检查。否则 this 会被认为是 any。
strict:true,
// 如果在构建工具中配置了路径解析别名,需要通过 compilerOptions.paths 为 TypeScript 再配置一遍。
paths:[
"@/*": ["src/*"],
]
}
}